<body>
    <script>
        //地图数组为map，可以根据oneD，twoD调整长宽
        var oneD = 35, twoD = 25;//地图大小
        var ZEROPOSSI = 50;//初始化过程中生成墙的概率

        var boxNUM = 1;//宝箱数量
        var stairNUM = 1;//楼梯数量
        var enermyNUM = 5;//敌人数量

        var map = new Array();//地图数组
        var needBOX = new Array();//箱子数组，存储坐标形式(x,y)，长度为boxNUM
        var needSTAIR = new Array();//楼梯数组
        var needENERMY = new Array();//敌人数组


        function max(a, b) { if (a > b) return a; else return b; }
        function min(a, b) { if (a < b) return a; else return b; }
        function abs(a) { if (a < 0) return -a; else return a; }

        function normalizearr(arr, origin) {
            for (var x = 0; x < oneD; x++)
                for (var y = 0; y < twoD; y++)
                    arr[x][y] = origin;
        }
        function ZeroPossi(zero_possi) {
            //生成地图时要设置黑色或者白色出现的概率
            //0 is wall,1 is road
            var x = Math.floor(Math.random() * 100);
            if (x <= zero_possi) {
                return 0;
            } else {
                return 1;
            }
        }

        //地图数组
        var MapIsChecked = new Array();
        for (var x = 0; x < oneD; x++) {
            map[x] = new Array(); MapIsChecked[x] = new Array();
            for (var y = 0; y < twoD; y++) {
                if (x == 0 || y == 0 || x == oneD - 1 || y == twoD - 1) map[x][y] = 0;
                else map[x][y] = ZeroPossi(ZEROPOSSI);
                MapIsChecked[x][y] = 0;
            }

        }
        
         
        //这个部分是在地图上显示黑白地图，由于没用注释掉
        
        function draw_line(v) {
        //生成零一数组后对指定位置(小方块）进行黑白填色，返回map数组的某一行元素
            var square = document.createElement("div");
            square.style.cssFloat = "left";
            square.style.width = 8;
            square.style.height = 8;
            if (v == 1)
                square.style.backgroundColor = "white";
            else if (v == 0)
                square.style.backgroundColor = "black";
            else if (v == 2)
                square.style.backgroundColor = "red";
            else if (v == 3)
                square.style.backgroundColor = "yellow";
            else if (v == 4)
                square.style.backgroundColor = "blue";
            square.style.border = ("1px solid #C0C0C0");
            return square;
        }
        function draw_square(arr) {
        //大方块填充，将map数组的每一行拼接起来
            for (var y = 0; y < twoD; y++) {
                var line = document.createElement("div");
                line.style.width = 10 * oneD;
                for (var x = 0; x < oneD; x++)
                    line.appendChild(draw_line(arr[x][y]));
                document.body.appendChild(line);
            }
        }
        

        //扩充白色区域
        function BiggerWhite() {
            normalizearr(MapIsChecked, 0);
            for (var x = 0; x < oneD; x++) {
                for (var y = 0; y < twoD; y++) {
                    if (map[x][y] == 1 && MapIsChecked[x][y] == 0) {
                        MapIsChecked[x][y] = 1;
                        for (var i = x - 1; i <= x + 1; i++) {
                            if (i < 0 || i >= oneD) continue;
                            for (j = y - 1; j <= y + 1; j++) {
                                if (j < 0 || j >= twoD) continue;
                                map[i][j] = MapIsChecked[i][j]=1;
                            }
                        }
                    }
                }
            }
        }
        



        //接下来这个部分是将杂乱生成的随机黑白图变得趋近于洞穴
        function CountWallSurround(arr, pos_x, pos_y, CountWidth) {
            //统计地图中，以某个位置的方块为圆心，width为半径的区域内有多少墙
            var count = 0;
            for (var i = pos_x - CountWidth; i <= pos_x + CountWidth; i++) {
                if (i < 0 || i >= oneD) continue;
                for (var j = pos_y - CountWidth; j <= pos_y + CountWidth; j++) {
                    if (j < 0 || j >= twoD) continue;
                    count += arr[i][j];
                }
            }
            return count;
        }

        function TrueCountWallSurround(arr, pos_x, pos_y, CountWidth) {
            //统计地图中，以某个位置的方块为圆心，width为半径的区域内有多少墙
            var count = 0;
            for (var i = pos_x - CountWidth; i <= pos_x + CountWidth; i++) {
                if (i < 0 || i >= oneD) continue;
                for (var j = pos_y - CountWidth; j <= pos_y + CountWidth; j++) {
                    if (j < 0 || j >= twoD) continue;
                    count += !arr[i][j];
                }
            }
            return count;
        }

        function ChangeMap(posx, posy) {
            //生成地图后对其进行操作使之更加逼近洞穴
            //对于一个方块，如果周围方块中墙的数量超过某个值，则其变成方块；反之，变成道路
            //暂时性地图数组，以防函数中同时修改map的值和检查map墙的数量
            var TempMap = new Array();
            for (var x = 0; x < oneD; x++) {
                TempMap[x] = new Array();
                for (var y = 0; y < twoD; y++)
                    TempMap[x][y] = map[x][y];
            }
            for (var x = 0; x < oneD; x++) {
                for (var y = 0; y < twoD; y++) {
                    var WallSurround = CountWallSurround(map, x, y, 1);
                    if (TempMap[x][y] == 0) TempMap[x][y] = (WallSurround >= 4) ? 0 : 1;
                    else TempMap[x][y] = (WallSurround >= 5) ? 0 : 1;
                }
            }
            map = TempMap;
        }





        //接下来这部分是将较小的联通区域填充为墙
        //接下来这部分是将较小的联通区域填充为墙
        //接下来这部分是将较小的联通区域填充为墙
        //首先要定义函数CountConnectedBlock返回包含某个方块的联通块有多大
        //自然地，在检查的时候要用到向四个方向递归搜索的算法
        //检查过的要将对应的MapIsChecked掷为true，防止重复递归
        //统计联通块大小的函数
        //else体里面说明map中的[posx,posy]为路，则将其设为检查过

        //返回四个方向的联通块个数
        function CountConnectBlock(posx, posy) {
            if (posx < 0 || posx >= oneD || posy < 0 || posy >= twoD || map[posx][posy] == 0 || MapIsChecked[posx][posy] == true) return 0;
            else {
                MapIsChecked[posx][posy] = true;
                return 1 + CountConnectBlock(posx - 1, posy) + CountConnectBlock(posx + 1, posy) + CountConnectBlock(posx, posy - 1) + CountConnectBlock(posx, posy + 1);
            }
        }

        //将包含(posx,posy)的联通块填充的函数
        function BlockFill(posx, posy) {
            if (posx < 0 || posx >= oneD || posy < 0 || posy >= twoD || map[posx][posy] == 0) return 0;
            else {
                map[posx][posy] = 0;
                return BlockFill(posx - 1, posy) + BlockFill(posx + 1, posy) + BlockFill(posx, posy - 1) + BlockFill(posx, posy + 1);
            }
        }

        //检索map填充block
        function CheckAndFill(size_atleast) {
            normalizearr(MapIsChecked, 0);
            for (var x = 0; x < oneD; x++) {
                for (var y = 0; y < twoD; y++) {
                    if (map[x][y] == 1 && MapIsChecked[x][y] == false) {
                        if (CountConnectBlock(x, y) < size_atleast) BlockFill(x, y);
                    }
                }
            }
        }





        //接下这部分使用bezier连接各个不相连的block
        //接下这部分使用bezier连接各个不相连的block
        //接下这部分使用bezier连接各个不相连的block
        //首先建立BlockTree储存block，BlockNum表示存在的Blocks的个数
        //AddNode就是把某个像素块添加到所属的BlockTree中，每个Block有自己的编号
        var BlockNum = 0, len = 0;
        var BlockTree = new Array();
        BlockTree[BlockNum] = new Array();

        function coordinate(x, y) { this.x = x; this.y = y; }

        function AddNode(posx, posy) {
            if (posx < 0 || posx >= oneD || posy < 0 || posy >= twoD || map[posx][posy] == 0 || MapIsChecked[posx][posy] == true) return 0;
            else {
                MapIsChecked[posx][posy] = 1;
                BlockTree[BlockNum][len] = new coordinate(posx, posy);
                len++;
                return AddNode(posx - 1, posy) + AddNode(posx + 1, posy) + AddNode(posx, posy - 1) + AddNode(posx, posy + 1);
            }
        }

        function BuildTree() {
            normalizearr(MapIsChecked, 0);
            for (var x = 0; x < oneD; x++) {
                for (var y = 0; y < twoD; y++)
                    if (map[x][y] == 1 && MapIsChecked[x][y] == false) { AddNode(x, y); BlockTree[++BlockNum] = new Array(); len = 0; }
            }
        }

        function bezeier(p1, p2, p0, arr) {
            var xtp = Math.round(p1.x), xt, ytp = Math.round(p1.y), yt;
            for (var t = 0; t <= 1; t += 0.005) {
                xt = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * p0.x + t * t * p2.x;
                yt = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * p0.y + t * t * p2.y;
                xt = Math.round(xt), yt = Math.round(yt);
                //console.log(t, xt, yt);
                arr[xt][yt] = 1;
                if (xt > 0 && xt < oneD - 1 && yt > 0 && yt < twoD - 1) {
                    arr[xt + 1][yt - 1] = 1;
                    arr[xt][yt - 1] = 1;
                    arr[xt + 1][yt] = 1;
                    arr[xt + 1][yt + 1] = 1;
                }
            }
        }

        function UnitBlock() {
            BuildTree();
            if (BlockNum < 2) return 0;
            //将编号为i和i+1的区域块联通
            for (var i = 0; i < BlockNum - 1; i++) {
                var pick_1 = BlockTree[i].pop();
                var pick_2 = BlockTree[i + 1].pop();
                //连接pick_1和pick_2
                var p0 = new coordinate();
                var x1 = pick_1.x, y1 = pick_1.x, x2 = pick_2.x, y2 = pick_2.y;
                var x0, y0;
                if (x1 != min(x1, x2)) { var c = x1; x1 = x2; x2 = c; c = y1; y1 = y2; y2 = c; }
                x0 = x1, y0 = y2;
                if (x2 - x1 <= 3 && abs(y2 - y1) <= 3) { }
                else if (x2 - x1 <= 4) {
                    if (x1 < oneD - x2) { x0 += (oneD - x2 - 3) };
                    if (x1 >= oneD - x2) x0 = 3;
                }
                else if (abs(y2 - y1) <= 4) {
                    var y22 = y2, y11 = y1;
                    if (y11 > y22) { var c = y11; y11 = y22; y22 = c; }
                    if (y11 < twoD - y22) { y0 += (oneD - y22 - 3) };
                    if (y11 >= twoD - y22) y0 = 3;
                }
                p0.x = x0; p0.y = y0;
                bezeier(pick_1, pick_2, p0, map);
                BlockTree[i].push(pick_1); BlockTree[i + 1].push(pick_2);
            }
        }





        var pointer1 = pointer2=0;
        var EnermyPossiblePos = new Array();
        var BoxPossiblePos = new Array();
        var StairPossiblePos = new Array();

        function CheckSpecificPos(arr) {
            for (var i = 0; i < oneD; i++) {
                for (var j = 0; j < twoD; j++) {
                    if (arr[i][j] && TrueCountWallSurround(arr, i, j, 1) == 0) {
                        EnermyPossiblePos[pointer1] = BoxPossiblePos[pointer1] = new coordinate(i, j);
                        pointer1++;
                    }
                    else if (arr[i][j] && TrueCountWallSurround(arr, i, j, 1) == 3)
                        StairPossiblePos[pointer2++] = new coordinate(i, j);
                }
            }
        }

        function produceENERMY(arr) {
            var ispicked = new Array();
            for (var i = 0; i <= pointer1; i++)ispicked[i] = 0;
            for (var i = 0; i < enermyNUM; i++) {
                var index = Math.floor(Math.random() * pointer1);
                if (!ispicked[index]) {
                    ispicked[index] = 0;
                    var x_pos = EnermyPossiblePos[index].x;
                    var y_pos = EnermyPossiblePos[index].y;
                    needENERMY[i] = new coordinate(x_pos, y_pos);
                    arr[x_pos][y_pos] = 4;
                }
                else {
                    i--;
                }
            }
        }

        function produceBOX(arr) {
            var ispicked = new Array();
            for (var i = 0; i <= pointer1; i++)ispicked[i] = 0;
            for (var i = 0; i < boxNUM; i++) {
                var index = Math.floor(Math.random() * pointer1);
                if (!ispicked[index]) {
                    ispicked[index] = 0;
                    var x_pos = BoxPossiblePos[index].x;
                    var y_pos = BoxPossiblePos[index].y;
                    needBOX[i] = new coordinate(x_pos, y_pos);
                    arr[x_pos][y_pos] = 3;
                }
                else {
                    i--;
                }
            }
        }

        function produceSTAIR(arr) {
            var ispicked = new Array();
            for (var i = 0; i <= pointer2; i++)ispicked[i] = 0;
            for (var i = 0; i < stairNUM; i++) {
                var index = Math.floor(Math.random() * pointer2);
                if (!ispicked[index]) {
                    ispicked[index] = 0;
                    var x_pos = StairPossiblePos[index].x;
                    var y_pos = StairPossiblePos[index].y;
                    needSTAIR[i] = new coordinate(x_pos, y_pos);
                    arr[x_pos][y_pos] = 2;
                }
                else {
                    i--;
                }
            }
        }
        //总函数
        function produce(arr) {
            CheckSpecificPos(arr);
            produceENERMY(arr);
            produceBOX(arr);
            produceSTAIR(arr);
        }

        //main过程
        //main过程
        //main过程
        //draw_square(map);
        var startx = Math.floor(oneD / 2), starty = Math.floor(twoD / 2);
        ChangeMap(startx, starty);
        ChangeMap(startx, starty);
        ChangeMap(startx, starty);
        ChangeMap(startx, starty);
        //draw_square(map);
        CheckAndFill(35);
        //draw_square(map);
        UnitBlock();
        //draw_square(map);
        produce(map);
        draw_square(map);
        //console.log(pointer);
    </script>
</body>